<?php namespace sacvoyage\app;

require_once 'sacvoyage/http/util.php';
use \sacvoyage\domain\Resource;
use \sacvoyage\Object;


class FileApplication extends Application {
	
	public function __construct(Resource $resource) {
		$this->_resource=$resource;
	}
	
	public function serve($path) {
		list($collectionName, $key)=\sacvoyage\http\parse_path($path);
		$key=\sacvoyage\http\ltrim_path($key);
		if($key) {
			$key=urldecode($key);
			$key=explode('~', $key, 2);
			if(count($key)==2) {
				list($id, $rev)=$key;
			} else {
				$id=$key[0];
				$rev=null;
			}		
		} else {
			$id=null;
			$rev=null;
		}

		
		//$collection=$this->_resource->getCollection($collectionName);
		if(strtoupper($_SERVER['REQUEST_METHOD']) == 'GET') {
			$this->get($collectionName, $id, $rev);
		} elseif(strtoupper($_SERVER['REQUEST_METHOD']) == 'POST') {
			$this->post($collectionName, $id, $rev);
		} elseif(strtoupper($_SERVER['REQUEST_METHOD']) == 'DELETE') {
			$this->delete($collectionName, $id, $rev);
		} else {
			echo '"unsupported method"';
		}
	}
	
	private function getChunkCollectionName($name) {
		if(!$name) {
			return 'db.chunks';
		} elseif(substr($name, strlen($name)-6)=='.files') { // end with .files
			return substr($name, 0, strlen($name)-6).'.chunks';
		} else {
			return rtrim($name, '.').'.chunks';
		}
	}
	
	public function get($collectionName, $id, $rev) {
		if(!in_array($collectionName, $this->_resource->getCollectionNames())
		|| !$id) {
			header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found", true);
			return;
		}
		
		$collection=$this->_resource->getCollection($collectionName);
		$document=$collection->read($id, $rev);
		
		if(is_object($document) && isset($document->content)) {
			if(!headers_sent()) {
				if(isset($document->content_type) 
				&& preg_match('@^\w+/[\w\-]+$@', $document->content_type)) {
					header('Content-type: '.$document->content_type, true);
				} else {
					header('Content-type: text/plain', true);
				}
			}
			if(isset($document->content_encoding) && $document->content_encoding=='base64') {
				echo base64_decode($document->content);
			} else {
				echo $document->content;
			}
		} else if(is_object($document) && isset($document->chunks)) {
			if(isset($document->content_type) 
			&& preg_match('@^\w+/[\w\-]+$@', $document->content_type)) {
				header('Content-type: '.$document->content_type, true);
			} else {
				header('Content-type: text/plain', true);
			}
			
			$chunkCollection=$this->_resource->getCollection(
				$this->getChunkCollectionName($collectionName)
			);
			foreach($document->chunks as $chunkId) {
				$chunk=$chunkCollection->read($chunkId);
				if(!$chunk) {
					header($_SERVER["SERVER_PROTOCOL"]." 404 File Chunk Not Found", true);
					return;
				}
				if(isset($document->content_encoding) && $document->content_encoding=='base64') {
					echo base64_decode($chunk->data);
				} else {
					echo $chunk->data;
				}
			}
		} elseif(is_object($document)) { 
			header('Content-type: application/json', true);
			echo \sacvoyage\http\save_json($document);
		} else {
			header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found", true);
			header('Content-type: application/json', true);
		}
	}
	

	public function post($collectionName, $id, $rev) {
		header('Content-type: application/json', true);
		$document=new Object();
		if($id) {
			$document->_id=$id;
		}
		if($rev) {
			$document->_rev=$rev;
		}
		$document->content_size=0;
		
		if(isset($_FILES) && isset($_FILES['_data'])) {
			$document->content_type=$_FILES['_data']['type'];
			if($_FILES['_data']['tmp_name']) {
				$file=fopen($_FILES['_data']['tmp_name'], 'r');
			} else {
				header($_SERVER["SERVER_PROTOCOL"]." 413 Request Entity Too Large", true);
				echo '{"status":"error","message":"uploaded file is too big"}';
				return;
			}			
		} elseif(isset($_POST['_data'])) {
			if(isset($_POST['_type'])) {
				$document->content_type=$_POST['_type'];
			} else {
				$document->content_type='text/plain';
			}
			$file=fopen('data:,'.$_POST['_data'], 'r');		
		} else {
			if(isset($_SERVER['CONTENT_TYPE'])) {
				$document->content_type=$_SERVER['CONTENT_TYPE'];
			} else {
				$document->content_type='application/octet-stream';
			}
			$file=fopen('php://input', 'r');
		}
		
		if(!$file) {
			header($_SERVER["SERVER_PROTOCOL"]." 500 Internal Server Error", true);
			echo '{"status":"error","message":"file upload error"}';
			return;
		}
		
		if(!isset($document->content_type)
		|| !preg_match('@^\w+/[\w\-]+$@', $document->content_type)) {
			$document->content_type='application/octet-stream';
		}
		
		$sha1=hash_init('sha1');
		do {
			$data='';
			while(!feof($file) && strlen($data)<256*1024) {
				$block=fread($file, 256*1024);
				hash_update($sha1, $block);
				$data.=$block;
			}
			
			$document->content_size+=strlen($data);
			
			// the whole file is written to document at once
			if(feof($file) && !isset($document->chunks)) {
				$document->content_encoding='base64';
				$document->content=base64_encode($data);
				break;
			} 
			// it's the cnunk
			else {
				$chunk=new Object(array(
					'size'=>strlen($data),
					'data'=>base64_encode($data)
				));
				if(!isset($chunkCollection)) {
					$chunkCollection=$this->_resource->getCollection(
						$this->getChunkCollectionName($collectionName)
					);
				}
				$written=$chunkCollection->write($chunk);
				foreach($written as $chunk) {
					$document->chunks[]=$chunk->_id;
				}
	
			}
		} while(!feof($file));
		$data='';
		fclose($file);
		
		$document->content_sha1=hash_final($sha1);
		$written=$this->_resource->getCollection($collectionName)->write($document);
		echo '{"status":"ok","written":'.(\sacvoyage\http\save_json($written)).'}';
	}
	
	public function delete($collectionName, $id, $rev){
		if(!in_array($collectionName, $this->_resource->getCollectionNames())
		|| !$id) {
			header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found", true);
			return;
		}
		$collection=$this->_resource->getCollection($collectionName);
		
		
		$query=new Object(array(
			'filter'=>new Object(array(
				'_id'=>$id
			)),
			'select'=>new Object(array(
				'_rev'=>true,
				'chunks'=>true
			))
		));	
		if($rev) {
			$query->status="all";
			$query->filter->_rev=$rev;
		}
		
		$document=$collection->find($query);
		if(!$document) {
			header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found", true);
		}
		$document=$document[0];
		
		if(isset($document->chunks)) {
			$chunkCollection=$this->_resource->getCollection(
				$this->getChunkCollectionName($collectionName)
			);
			$chunkCollection>delete(new Object(array(
				'filter'=>new Object(array(
					'_id'=> new Object(array(
						'_or'=>$document->chunks // chunks is array if strings
					))
				))
			)));
		} 
		
		$collection->delete($query);
		
	}
	
	
}


?>
